#前言
本文写于 TailwindCSS V4 + Vite + Vue3 环境
我之所以使用 TailwindCSS 编写组件又使用 @apply
将样式写在 css 文件而不是写在 class
属性内原因有两点
- 方便覆盖,如果写在
class
属性内只能通过引入tailwind-merge
来动态解析哪些冲突,这是运行时的行为,感觉会多出一比无意义的性能开销 - 解决 class 太长问题,在组件中写 tailwindcss 常见的就是 class 名太长太多了,非常混乱还超过了 printwidth 80,阅读及其难受
为什么不放弃使用 tailwindcss
懒,能少写一点是一点,顺便用 tailwindcss 定义好的颜色组等
Vue 支持内联 CSS 因此组件的样式基本写在 <style>
标签内
#样式覆盖问题
组件定义样式后,用户使用 TailwindCSS 样式自定义组件某些样式时会无法覆盖,因为其优先级会低于组件样式的优先级。而使用 TailwindCSS 时除了使用 important 或写 CSS 文件基本无法解决,因此需要编写组件时降低样式的优先级
降低优先级的解决方案:
#CSS where
选择器
使用 where
选择器 (MDN 文档) 可以始终将优先级设置为 0,比如
:where(.ui-button) {
@apply inline-flex;
}
非常合适用于避免和用户自定义样式冲突
#CSS layer
特性
layer 是 css 的一个实验性特性 (MDN 文档)。
其作用用于组织样式到不同的层中,layer 会按照声明定义顺序,后声明的 layer 优先级会高于前面的。
使用 Tailwind 基本都会用到 layer, Tailwind 中默认定义了 4 种 Layer,分别是 theme
、base
、components
、utilities
因此可以使用 components
layer 来定义组件样式
@layer components{
:where(.ui-button) {
@apply inline-flex;
}
}
这样就能确保该组件不会和用户自定义样式冲突,就算没有 where
选择器也不会和 utilities
层的例如 px-2
等起冲突。
使用 layer 就必需确保 layer 顺序正确,然而很不幸被打乱了
#layer 顺序被打乱问题
TailwindCSS 所使用的 Layer 顺序可以在 tailwindcss/index.css
第一行看到
@layer theme, base, components, utilities;
因此顺序便是
theme -> base -> components -> utilities
像常见的 p-2
、flex
等都是定义在 utilities
layer 中的,优先级是这几个 layer 中最高的
在 Vue 中使用 style
标签默认会在组件挂载时将样式注入到 <head>
末尾,这就可能导致组件样式会比 global.css
提前注入到 head
中。
比如 toast
这种常见于全局挂载的组件,当这种组件中使用了 components
layer 就会导致 layer 顺序变成了
components -> theme -> base -> utilities
这就会导致某些样式比如 tailwindcss 中使用 base
层的 prefight 覆盖了组件样式,如上示例可以看到打乱后的 base
层比 components
层高。
为什么会出现这种情况,其实是加载顺序的问题,通常导入 CSS 语句写在导入末尾的,例如
import { createApp } from 'vue';
import App from './pages/app.vue';
import "./global.css"
createApp(App).mount('#app');
App
组件会先于 global.css
加载,而在 App
中使用的全局组件则会先一步被注入到 <head>
标签内
为了解决该问题,只能通过确保 global.css
优先加载比如将其置顶
import "./global.css";
import { createApp } from 'vue';
...
或者将 layer 声明内联定义在入口 HTML head
标签内
<head>
<style>
@layer base, components, utilities;
</style>
</head>
也可以在入口 HTML 直接引入 global.css
文件
<head>
<link rel="stylesheet" type="text/css" href="/src/global.css"/>
</head>